//
//  JWPicker.m
//  wolf3d
//
//  Created by James Weatherley on 09/10/2009.
//  Copyright 2009 James Weatherley. All rights reserved.
//

#import "JWPicker.h"
#import "JWSquare.h"
#import "JWLandscape.h"
#import "../jwglut/gluUnProject.h"
#import "../wolfiphone.h"

@implementation JWPicker

@synthesize landscape;

static JWPicker* s_picker = nil;


+(JWPicker*)picker
{
	return s_picker;
}

-(id)initWithLandscape:(JWLandscape*)land
{
	assert(!s_picker);
	if(self == [super init]) {
		landscape = land;
		[landscape retain];
		s_picker = self;
	}
	return s_picker;
}

-(void)changeLandscape:(JWLandscape*)land
{
	if(landscape != land) {
		[landscape release];
		landscape = land;
		[landscape retain];
	}
}

-(JWSquare*)pickUsingScreenCoordinates:(CGPoint)point
{
	JWSquare* square = nil;
	
	GLfloat model[16];
	GLfloat projection[16];
	GLint viewport[4];
	
	vec3_t world0;
	vec3_t world1;
	vec3_t vec;
	
	// Look up, down, left and right of selected point to assist fat fingers. 
	CGPoint adjustedPoint;
	static NSInteger deltas[] = {0, 0, 1, 0, 0, 1, -1, 0, 0, -1};
	static NSUInteger size = sizeof(deltas) / sizeof(NSInteger);
	
	for(int i = 0; i < size; ++i) {
		adjustedPoint = point;
		adjustedPoint.x += deltas[i * 2];
		adjustedPoint.y += deltas[i * 2 + 1];
	
		qglGetFloatv(GL_MODELVIEW_MATRIX, model);
		qglGetFloatv(GL_PROJECTION_MATRIX, projection);
		qglGetIntegerv(GL_VIEWPORT, viewport);
		
		gluUnProject(viewport[2] / 2.0f, viewport[3] / 2.0f, 0.0f,
					 model, projection, viewport,
					 world0, world0 + 1, world0 + 2);
		
		gluUnProject(point.y, point.x, 1.0f,
					 model, projection, viewport,
					 world1, world1 + 1, world1 + 2);
		
		vectorSubtract(world1, world0, vec);
		_VectorNormalize(vec);
		
		if(!revLand->value) {
			//vec[1] = -vec[1];
		} else {
			//vec[2] = -vec[2];
		}
		
		square = [self pickUsingWorldCoordinatesAlong:vec from:world0];
		
		if(square) {
			break;
		}
	}
	return square;
}

-(NSArray*)squaresInPickViewAlong:(vec3_t)vector3 from:(vec3_t)point3
{	
	// This could probably be done better by adapting the code in wolf_raycast.c
	// Profiling suggests that it doesn't really matter though.
	
	// Landscape is based on an xz plane.
	NSMutableArray* squares = [[[NSMutableArray alloc] initWithCapacity:32] autorelease];
	float theta = atan2(vector3[2], vector3[0]);
	
	float sinTheta = sin(theta);
	float cosTheta = cos(theta);
	float delta = 0.2f;
	
	float x = point3[0];
	float z = point3[2];
	int oldX = x;
	int oldZ = z;
	CGSize size = [landscape dimensions];
	
	JWSquare* square = [landscape getSquareAtX:oldX z:oldZ];
	if(square) {
		[squares addObject:square];
	}
	
	while(x > 0 && x < size.height && z > 0 && z < size.width) {
		if((int)x != oldX || (int)z != oldZ) {
			oldX = x;
			oldZ = z;
			[squares addObject:[landscape getSquareAtX:oldX z:oldZ]];
		}
		x += delta * cosTheta;
		z += delta * sinTheta;
	}
	
	return squares;
}

-(JWSquare*)pickUsingWorldCoordinatesAlong:(GLfloat*)vector3 from:(GLfloat*)point3
{
	static BOOL recurseNoMore = NO;
	JWSquare* square = nil;
	NSArray* squares = [self squaresInPickViewAlong:vector3 from:point3];
	
	for(square in squares) {
		if(![square.boxes count] && square.height >= Player.position.height) {
			continue;
		}
		
		if([square intersectWithRayFromPoint:point3 direction:vector3]) {
			break;
		}
		if(square.flat && [square boxCount]) {
			// Boxes are treated as an extension of the square
			if([square intersectBoundingBoxWithRayFromPoint:point3 direction:vector3 boxesOnly:YES]) {
				break;
			}
		}
		if(square.flat && (square.tree || square.robot || square.sentinel)) {
			if([square intersectBoundingBoxWithRayFromPoint:point3 direction:vector3 boxesOnly:NO]) {
				vec3_t ray;
				vec3_t vertices[4];
				[square getVerticesA:vertices[0] B:vertices[1] C:vertices[2] D:vertices[3]];
				
				for(int i = 0; i < 4; ++i) {
					vectorSubtract(vertices[i], point3, ray);
					
					if(!recurseNoMore) {
						recurseNoMore = YES;
						if([self pickUsingWorldCoordinatesAlong:ray from:point3] == square) {
							goto bail;
						}
					}
				}
			}
		}
	}
	
bail:
	// Hacky anti-recursion flags and a goto. I'm off to coding hell!
	recurseNoMore = NO;
	return square;
}

-(void)clearSelection
{
	landscape.selectedSquare.selected = NO;
}

-(void)dealloc
{
	[landscape release];
	[super dealloc];
}

@end
